<!-- MyUploader.vue -->
<template>
    <view class="my-uploader-container">
        <!-- 将自定义的内容放入 uv-upload 标签内，它会自动填充到默认插槽中 -->
        <uv-upload :fileList="fileList" :name="name" :multiple="multiple" :maxCount="maxCount" :accept="accept"
            @afterRead="afterRead" @delete="deletePic">
            <view class="custom-upload-btn">
                <uv-icon name="plus" color="#909399" size="26"></uv-icon>
            </view>

        </uv-upload>
    </view>
</template>

<script>
export default {
    name: "MyUploader",
    props: {
        value: { type: Array, default: () => [] },
        action: { type: String, required: true },
        maxCount: { type: Number, default: 9 },
        multiple: { type: Boolean, default: true },
        accept: { type: String, default: 'image' },
        formData: { type: Object, default: () => ({}) },
        name: { type: [String, Number], default: 'default' },
        //允许的文件后缀名数组，例如 ['jpg', 'png']，空数组则不限制
        allowedExtensions: {
            type: Array,
            default: () => []
        },
        //单个文件最大尺寸，单位：字节 (bytes)，0则不限制
        maxSize: {
            type: Number,
            default: 0
        }
    },
    data() {
        return {
            fileList: []
        };
    },
    watch: {
        value: {
            handler(newVal) { this.fileList = newVal; },
            immediate: true,
            deep: true
        }
    },
    methods: {
        updateValue() { this.$emit('input', this.fileList); },
        deletePic(event) {
            this.fileList.splice(event.index, 1);
            this.updateValue();
        },
        async afterRead(event) {
            let selectedFiles = [].concat(event.file);
            const validFiles = this.filterValidFiles(selectedFiles);
            if (validFiles.length === 0) {
                return;
            }
            const lists = validFiles;
            let fileListLen = this.fileList.length;

            lists.forEach(item => {
                this.fileList.push({ ...item, status: 'uploading', message: '上传中...' });
            });

            for (let i = 0; i < lists.length; i++) {
                const currentFile = lists[i];
                const currentIndex = fileListLen + i;
                try {
                    const resultUrl = await this.uploadFilePromise(currentFile.url);
                    const successFileObject = {
                        ...currentFile,
                        status: 'success',
                        message: '上传成功',
                        url: resultUrl,
                        thumb: resultUrl
                    };
                    this.fileList.splice(currentIndex, 1, successFileObject);
                } catch (errorMessage) {
                    this.fileList.splice(currentIndex, 1, { ...currentFile, status: 'failed', message: errorMessage || '上传失败' });
                }
            }
            this.updateValue();
        },
        /**
         * 【新增】文件校验过滤器
         * @param {Array} files - 从 @afterRead 获取的原始文件列表
         * @returns {Array} - 通过校验的文件列表
         */
        filterValidFiles(files) {
            const validationRules = [
                {
                    // 规则1：校验文件大小
                    validator: (file) => this.maxSize === 0 || file.size <= this.maxSize,
                    message: `文件大小不能超过 ${this.formatFileSize(this.maxSize)}`
                },
                {
                    // 规则2：校验文件后缀
                    validator: (file) => {
                        if (this.allowedExtensions.length === 0) return true;
                        const extension = this.getFileExtension(file.name || file.url);
                        return this.allowedExtensions.map(ext => ext.toLowerCase()).includes(extension);
                    },
                    message: `不支持该文件类型，请上传 ${this.allowedExtensions.join(', ')} 格式的文件`
                }
            ];

            return files.filter(file => {
                for (const rule of validationRules) {
                    if (!rule.validator(file)) {
                        uni.showToast({
                            title: rule.message,
                            icon: 'none',
                            duration: 2500
                        });
                        return false; // 未通过校验
                    }
                }
                return true; // 所有校验规则都通过
            });
        },

        /**
         * 获取文件后缀名 (转为小写)
         */
        getFileExtension(filename) {
            if (typeof filename !== 'string' || !filename) return '';
            // 从最后一个 '.' 后面截取，并转为小写
            return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
        },
        /**
         * 格式化文件大小，用于友好提示
         */
        formatFileSize(bytes) {
            if (bytes === 0) return '0 B';
            const k = 1024;
            const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        },
        uploadFilePromise(filePath) {
            return new Promise((resolve, reject) => {
                uni.uploadFile({
                    url: this.action,
                    filePath: filePath,
                    name: 'file',
                    header: {
                        'token': this.token
                    },
                    formData: this.formData,
                    success: (res) => {
                        if (res.statusCode === 200) {
                            const serverData = JSON.parse(res.data);
                            if (serverData.code === 200) {
                                console.log(this.baseUrl);
                                resolve(this.sanitizeUrl(this.baseUrl + serverData.data.file));
                            } else {
                                reject(serverData.msg || '服务器处理失败');
                            }
                        } else {
                            reject('请求失败，状态码：' + res.statusCode);
                        }
                    },
                    fail: (err) => { reject('上传请求发送失败'); }
                });
            });
        },
        /**
        * 规范化URL，移除路径中多余的斜杠，但保留协议中的 '//'
        * 例如：'https://domain.com//path/to//resource' -> 'https://domain.com/path/to/resource'
        * @param {string} urlString 待处理的URL字符串
        * @returns {string} 处理后的URL字符串
        */
        sanitizeUrl(urlString) {
            if (typeof urlString !== 'string' || !urlString) {
                return '';
            }
            // 使用正则表达式：
            // \/\/  -> 匹配两个连续的斜杠
            // (?<!:) -> 负向先行断言，确保这两个斜杠前面没有冒号 ':'
            // g       -> 全局匹配，替换所有满足条件的实例
            return urlString.replace(/(?<!:)\/\//g, '/');
        }

    }
}
</script>

<!-- style 部分也需要修改，样式作用于我们自己的 view 上 -->
<style scoped>
.my-uploader-container {
    width: 100%;
}

/* 为我们自定义的按钮添加样式 */
.custom-upload-btn {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    /* 尺寸 */
    width: 160rpx;
    /* 宽度可自定义 */
    height: 160rpx;
    /* 高度可自定义 */
    /* 虚线边框 */
    border: 2rpx dashed #e5e5e5;
    /* 其他样式 */
    background-color: #ffffff;
    border-radius: 4px;
}

.btn-text {
    font-size: 12px;
    color: #909399;
    margin-top: 4px;
}
</style>